# -*- coding: utf-8 -*-
# 本资源来源于互联网公开渠道，仅可用于个人学习爬虫技术。
# 严禁将其用于任何商业用途，下载后请于 24 小时内删除，搜索结果均来自源站，本人不承担任何责任。

from base.spider import Spider
from Crypto.Cipher import ARC4,AES
from urllib.parse import quote_plus
from Crypto.Util.Padding import unpad
import re,sys,time,uuid,json,base64,urllib3,hashlib,secrets,binascii
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
sys.path.append('..')

class Spider(Spider):
    headers = {
        'User-Agent': "Dalvik/2.1.0",
        'Connection': "Keep-Alive",
        'Accept-Encoding': "gzip"
    }
    headers2 = {**headers}
    client_mode,max_client,app_id,pg_url,yry_url,client,base_path,pg_key,yry_key,token,user,pwd,markcode,login_mode,app_name,model = 0,2,'','','','','','','','','','','','','',''

    def init(self, extend=''):
        try:
            ext = json.loads(extend)
        except Exception:
            ext = {}
        self.user = ext.get('username')
        self.pwd = ext.get('password')
        self.markcode = ext.get('markcode')
        self.app_name = ext.get('app_name', 'xiaomi')
        self.model = ext.get('model', 'xiaomi')
        cos_url = ext.get('CosUrl', 'http://2025-1329689796.cos.ap-guangzhou.myqcloud.com/kanke/app1.json')
        try:
            number1 = 'No00000'
            number = self.md5(number1)
            number_key = self.md5(f'{number1}SmtEk1')
            print('number:', number, '| numberkey:', number_key)
            payload = {'t': str(int(time.time())), 'key': self.rc4_encrypt(number, number_key)}
            self.app_id = '10000'
            res = self.fetch(cos_url, headers=self.headers, verify=False).json()
            notice = res['msg']['notice']
            main_host = self.decode(notice['content'])
            self.headers2['Authorization'] = self.decode(notice['type'])
            response = self.post(f'{main_host}?app={self.app_id}', data=payload, headers=self.headers2, verify=False).json()
            data = response['data']
            maink = self.rc4_decrypt(data['Maink'], number_key)
            self.pg_key = self.rc4_decrypt(data['pg'], maink)
            self.yry_key = self.rc4_decrypt(data['yry'], maink)
            print('pg_key:', self.pg_key, '| yry_key:', self.yry_key)
            key_time = self.rc4_decrypt(data['MT'], maink)
            pg_raw_url = data['pgUrl']
            yry_raw_url = data['yryUrl']
            aes_key = self.base64_encode(f"{key_time}{pg_raw_url[:14]}")
            aes_iv = self.base64_encode(f'{key_time}{yry_raw_url[:2]}').ljust(16)
            pg_url = self.aes_decrypt(self.base64_decode(pg_raw_url[32:]), aes_key, aes_iv)
            yry_url = self.aes_decrypt(self.base64_decode(yry_raw_url[16:]), aes_key, aes_iv)
            print('pg_url:', pg_url)
            print('yry_url:', yry_url)
            self.base_path = self.rc4_decrypt(data['HOST'])
            print('main_path:', self.base_path)
            self.client = self.rc4_decrypt(data['newClient'])
            print('client:', self.client)
            self.login_mode = data.get('Login', 4)
            self.pg_url = pg_url
            self.yry_url = yry_url
        except Exception:
            self.pg_url = ''

    def homeContent(self, filter):
        if not self.pg_url: return None
        response = self.fetch(f'{self.pg_url}/api.php/{self.base_path}/Category?{self.sign()}', headers=self.headers2, verify=False).text
        try:
            data = self.rc4_decrypt(response)
            data2 = json.loads(data)
        except Exception:
            data2 = json.loads(response)
        classes = []
        for i in data2:
            if isinstance(i,dict) and i['type_status'] == 1:
                classes.append({'type_id': i['type_en'], 'type_name': i['type_name']})
        return {'class': classes}

    def homeVideoContent(self):
        if not self.pg_url: return None
        response = self.fetch(f'{self.pg_url}/api.php/{self.base_path}/top?{self.sign()}', headers=self.headers2, verify=False).json()
        videos = self.arr2vods(response['data'])
        return {'list': videos}

    def categoryContent(self, tid, pg, filter, extend):
        if not self.pg_url: return None
        response = self.fetch(f'{self.pg_url}/api.php/{self.base_path}/vod/?ac=list&class={tid}&page={pg}&{self.sign()}', headers=self.headers2, verify=False).text
        try:
            data = self.rc4_decrypt(response)
            data2 = json.loads(data)
        except Exception:
            data2 = json.loads(response)
        videos = self.arr2vods(data2['data'])
        return {'list': videos,'pagecount':data2['totalpage'], 'page': pg}

    def searchContent(self, key, quick, pg='1'):
        if not self.pg_url: return None
        response = self.fetch(f'{self.pg_url}/api.php/{self.base_path}/So/?ac=list&zm={key}&page={pg}&{self.sign()}', headers=self.headers2, verify=False).text
        try:
            data = self.rc4_decrypt(response)
            data2 = json.loads(data)
        except Exception:
            data2 = json.loads(response)
        videos = self.arr2vods(data2['data'])
        return {'list': videos, 'page': pg}

    def detailContent(self, ids):
        if not self.pg_url: return None
        self.client_mode = 0
        self.auto_logon()
        req_data = f'token={self.token}&t={int(time.time())}'
        payload = {
            'data': self.rc4_encrypt(req_data),
            'sign': self.md5(f'{req_data}&{self.pg_key}')
        }
        res = self.post(f'{self.yry_url}/api.php?app={self.app_id}&act=motion',data=payload, headers=self.headers2, verify=False).json()
        self.client_mode = int(res['msg']['Clientmode'])
        if self.client_mode == 0 and res['msg']['Try'] != 1: return None
        response = self.fetch(f'{self.pg_url}/api.php/{self.base_path}/vod/{ids[0]}&{self.sign()}', headers=self.headers2, verify=False).text
        try:
            data2 = json.loads(response)
        except Exception:
            data = self.rc4_decrypt(response)
            data2 = json.loads(data)
        try:
            player_ = self.rc4_decrypt(data2['player'])
            player = json.loads(player_)
        except Exception:
            player = json.loads(response['player'])
        vod_name = data2['title']
        shows, play_urls = [], []
        for show, raw_urls in data2['videolist'].items():
            name = None
            for i in player:
                if i['from'] == show and i['from'] != i['show']:
                    name = i['show']
                    break
            urls = []
            for j in raw_urls:
                title = j['title']
                series = quote_plus(f"{vod_name}-{title}")
                urls.append(f"{title}${series}@{j['url']}")
            play_urls.append('#'.join(urls))
            if name:
                shows.append(f'{name}\u2005({show})')
            else:
                shows.append(show)
        video = {
            'vod_id': ids[0],
            'vod_name': vod_name,
            'vod_pic': data2['img_url'],
            'vod_remarks': data2['trunk'],
            'vod_year': data2['pubtime'],
            'vod_area': ','.join(data2['area']),
            'vod_actor': ','.join(data2['actor']),
            'vod_director': ','.join(data2['director']),
            'vod_content': data2['intro'],
            'vod_play_from': '$$$'.join(shows),
            'vod_play_url': '$$$'.join(play_urls),
            'type_name': ','.join(data2['type'])
        }
        return {'list': [video]}

    def playerContent(self, flag, vid, vip_flags):
        jx,url = 0,''
        series, raw_url = vid.split('@',1)
        self.client_mode = 1
        base_path = f'&account={self.user}&password={self.pwd}&series={series}&edition=1.0'
        cont = 0
        for i in range(1,4):
            if cont > self.max_client:
                break
            try:
                if self.client.startswith('http'):
                    if i == 1:
                        path = f'{self.client}/?url={raw_url}'
                    else:
                        path = f'{self.client}{i}/?url={raw_url}'
                    if self.client_mode == 1:
                        payload = {
                            'app': self.app_id,
                            'key': self.rc4_encrypt(base_path),
                            '': ""
                        }
                        response = self.post(path, data=payload, headers=self.headers2, verify=False).text
                    else:
                        response = self.post(f'{path}&app={self.app_id}{base_path}', headers=self.headers2, verify=False).text
                    try:
                        data = self.rc4_decrypt(response)
                        data2 = json.loads(data)
                    except Exception:
                        data2 = json.loads(response)
                    self.max_client = int(data2.get('maxClient'))
                    play_url = data2['url']
                    if play_url.startswith('http'):
                        url = play_url
                        break
                cont += 1
            except Exception:
                url = ''
                cont += 1
                continue
        if not url:
            try:
                play_url = self.rc4_decrypt(raw_url)
                if re.search(r'(?:www\.iqiyi|v\.qq|v\.youku|www\.mgtv|www\.bilibili)\.com', play_url):
                    jx, url = 1, play_url
            except Exception:
                pass
        play_headers = {
            'User-Agent': "Windows",
            'Connection': "close",
            'Range': "bytes=0-",
            'allowCrossProtocolRedirects': "true"
        }
        return { 'jx': jx, 'parse': 0, 'url': url, 'header': play_headers}

    def auto_logon(self):
        if not self.pg_url: return
        if self.user and self.pwd and self.markcode:
            user, pwd, markcode = self.user, self.pwd, self.markcode
        else:
            account_cache_key = f'smtv_account_com.kanke.tv_V73xfmWkXa'
            try:
                account = self.getCache(account_cache_key)
                user, pwd, markcode = account['username'], account['password'], account['markcode']
            except Exception:
                user, pwd, markcode = None, None, None
            if not (user and pwd and markcode):
                if self.login_mode == 2:
                    random_account = self.android_id()
                    user, pwd, markcode = random_account, random_account, random_account
                elif self.login_mode == 3:
                    random_account = self.mac()
                    user, pwd, markcode = random_account, random_account, random_account
                else:
                    random_account = self.uuid_number()
                    user, pwd, markcode = random_account, random_account, random_account
                self.register(user, pwd, markcode)
                self.setCache(account_cache_key, {'username': user, 'password': pwd, 'markcode': markcode})
        self.login(user, pwd, markcode)

    def login(self, user,pwd,markcode):
        if not self.pg_url: return
        req_data = f'account={user}&password={pwd}&markcode={markcode}&t={int(time.time())}'
        payload = {
            'data': self.rc4_encrypt(req_data),
            'sign': self.md5(f'{req_data}&{self.pg_key}')
        }
        response = self.post(f'{self.yry_url}/api.php?app={self.app_id}&act=user_logon', data=payload, headers=self.headers2, verify=False).json()
        msg = response['msg']
        try:
            data = self.rc4_decrypt(msg, self.yry_key)
            data2 = json.loads(data)
        except Exception:
            data2 = json.loads(msg)
        self.token = data2['token']
        self.user = user
        self.pwd = pwd

    def register(self, user,pwd,markcode):
        if not self.pg_url: return
        user_host = self.fetch(f'{self.yry_url}/ip.json', headers=self.headers).text
        req_data = f'user={user}&password={pwd}&markcode={markcode}&t={int(time.time())}&name=看客&phone=xiaomi'
        payload = {
            'data': self.rc4_encrypt(req_data),
            'sign': self.md5(f'{req_data}&{self.pg_key}')
        }
        self.post(f'http://{user_host}/api.php?app={self.app_id}&act=user_reg', data=payload, headers=self.headers2, verify=False)

    def arr2vods(self, arr):
        videos = []
        if isinstance(arr,list):
            for i in arr:
                videos.append({
                    'vod_id': i.get('tjurl', i.get('nextlink')),
                    'vod_name': i.get('tjinfo', i.get('title')),
                    'vod_pic': i.get('tjpicurl', i.get('pic')),
                    'vod_remarks': i.get('state'),
                    'vod_year': None
                })
        return videos

    def rc4_encrypt(self, plaintext, key=None):
        if not key: key = self.yry_key
        key_bytes = key.encode('utf-8')
        plaintext_bytes = plaintext.encode('utf-8')
        cipher = ARC4.new(key_bytes)
        ciphertext = cipher.encrypt(plaintext_bytes)
        return binascii.hexlify(ciphertext).decode('utf-8')

    def rc4_decrypt(self,ciphertext_hex, key=None):
        if not key: key = self.pg_key
        key_bytes = key.encode('utf-8')
        ciphertext_bytes = binascii.unhexlify(ciphertext_hex)
        cipher = ARC4.new(key_bytes)
        plaintext_bytes = cipher.decrypt(ciphertext_bytes)
        return plaintext_bytes.decode('utf-8')

    def aes_decrypt(self, data, key, iv):
        try:
            encrypted_data = base64.b64decode(data)
            cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
            decrypted_padded = cipher.decrypt(encrypted_data)
            decrypted_data = unpad(decrypted_padded, AES.block_size)
            return decrypted_data.decode('utf-8')
        except Exception:
            raise ValueError

    def uuid_number(self):
        uuid_bytes = uuid.uuid4().bytes
        result = []
        for i in range(9):
            value = uuid_bytes[i] % 10
            result.append(str(value))
        return ''.join(result)

    def mac(self):
        mac_bytes = [secrets.randbits(8) for _ in range(6)]
        return ''.join(f"{byte:02X}" for byte in mac_bytes)

    def android_id(self):
        return ''.join(secrets.choice('0123456789abcdef') for _ in range(16))

    def sign(self):
        timestamp = str(int(time.time()))
        return f'key={self.rc4_encrypt(timestamp, timestamp)}&tt={timestamp}'

    def base64_encode(self, data):
        return base64.b64encode(data.encode('utf-8')).decode('utf-8')

    def base64_decode(self, data):
        return base64.b64decode(data.encode('utf-8')).decode('utf-8')

    def decode(self, data):
        return self.base64_decode(self.base64_decode(data[16:]))

    def md5(self, data):
        return hashlib.md5(data.encode('utf-8')).hexdigest()

    def getName(self):
        pass

    def isVideoFormat(self, url):
        pass

    def manualVideoCheck(self):
        pass

    def destroy(self):
        pass

    def localProxy(self, param):
        pass
